package com.example.demo1.util;

import com.alibaba.fastjson.JSONObject;
import com.google.api.client.http.HttpStatusCodes;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.springframework.stereotype.Component;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Map;

/**
 * @author zhangzichang
 * @Description: webservice调用Httpclient
 * @company：CTTIC
 * @since 2021/7/13 16:00
 */
@Slf4j
@Component
public class WebServiceHttpclient {

	/**
	 * 普通http连接的连接创建类
	 */
	private HttpClientBuilder httpClientBuilder;

	public static final String SERVICE_CONTENT_TYPE = "application/soap+xml; charset=utf-8";

	private int requestTimeout = 60000;
	private int socketTimeout = 60000;
	private int connectTimeout = 60000;



	public String postJson(String url, String json, Map<String, String> header) {
		HttpPost httpPost = new HttpPost(url);
		StringEntity data = new StringEntity(json, ContentType.APPLICATION_JSON);
		data.setContentType(ContentType.APPLICATION_JSON.getMimeType());
		data.setContentEncoding(ContentType.APPLICATION_JSON.getCharset().name());
		httpPost.setEntity(data);
		httpPost.setHeader("Accept", "application/json");
		httpPost.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType());
		return send(httpPost, header);
	}


	public String get(String url){
		HttpGet httpGet = new HttpGet(url);
		return send(httpGet);
	}

	public String getHeader(String url, Map<String, String> header){
		HttpGet httpGet = new HttpGet(url);
		return send(httpGet, header);
	}

	public String get(String url, Map<String, Object> param){
		return get(url, param, MapUtils.EMPTY_MAP);
	}

	public String get(String url, Map<String, Object> param, Map<String, String> header){
		log.info("======== httpclient get >>> param : {}", JSONObject.toJSON(param));
		URIBuilder ub = new URIBuilder();
		if (MapUtils.isNotEmpty(param)) {
			ArrayList<NameValuePair> pairs = covertParams2NVPS(param);
			ub.setParameters(pairs);
		}
		String responseStr = StringUtils.EMPTY;
		try {
			HttpGet httpGet = new HttpGet(url+ub.build());
			return send(httpGet, header);
		}
		catch (Exception e) {
			log.error(e.getMessage());
			return responseStr;
		}
	}

	private ArrayList<NameValuePair> covertParams2NVPS(Map<String, Object> params) {
		ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
		for (Map.Entry<String, Object> param : params.entrySet()) {
			pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
		}
		return pairs;
	}

	public String postJson(String url, Map<String, Object> param){
		log.info("======== httpclient post >>> param : {}", JSONObject.toJSON(param));
		return postJson(url, JSONObject.toJSONString(param));
	}


	public String send(HttpRequestBase requestBase){
		return send(requestBase, MapUtils.EMPTY_MAP);
	}
	public String send(HttpRequestBase requestBase, Map<String, String> header){
		String responseStr = StringUtils.EMPTY;
		HttpClient httpClient = wrapClient();
		HttpResponse response = null;
		try{
			if(MapUtils.isNotEmpty(header)){
				header.forEach(requestBase::addHeader);
			}
			response = httpClient.execute(requestBase);
			if (response == null) {
				log.error("response is null");
				return responseStr;
			}
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != HttpStatusCodes.STATUS_CODE_OK){
				log.error("接口请求失败,请检查网络设置，响应状态码：statusCode:{}", statusCode);
				return responseStr;
			}
			HttpEntity httpEntity = response.getEntity();
			responseStr = EntityUtils.toString(httpEntity, "UTF-8");
			log.info("============> resp : {}", responseStr);
			return  responseStr;
		}catch (Exception e){
			log.error("============> http send error: {}", e.getMessage(), e);
			return responseStr;
		}
	}

	public String postJson(String url, String param){
		HttpPost httpPost = new HttpPost(url);
		StringEntity data = new StringEntity(param, ContentType.APPLICATION_JSON);
		data.setContentType(ContentType.APPLICATION_JSON.getMimeType());
		data.setContentEncoding(ContentType.APPLICATION_JSON.getCharset().name());
		httpPost.setEntity(data);
		httpPost.setHeader("Accept", "application/json");
		httpPost.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType());
		return send(httpPost);
	}


	public String postForm(String url, Map<String, Object> param, Map<String, String> header) throws Exception {
		log.info("======== httpclient post >>> param : {}", JSONObject.toJSON(param));
		ArrayList<NameValuePair> nameValuePairs = covertParams2NVPS(param);
		return postForm(url, nameValuePairs, header);
	}

	public String postForm(String url, ArrayList<NameValuePair> nameValuePairs, Map<String, String> header) throws Exception {
		HttpPost httpPost = new HttpPost(url);
		UrlEncodedFormEntity data = new UrlEncodedFormEntity(nameValuePairs, "UTF-8");
		httpPost.setEntity(data);
		return send(httpPost, header);
	}

	public String invoke(String webServiceUrl, String soapXml){
		log.info("============>webServiceUrl: {} ,  req : {}", webServiceUrl, soapXml);
		// 创建HttpClientBuilder
		String responseStr = StringUtils.EMPTY;
		HttpResponse response = null;
		try {
			HttpPost httpPost = new HttpPost(webServiceUrl);
			HttpClient httpClient = wrapClient();
			StringEntity data = new StringEntity(soapXml, Charset.forName("UTF-8"));
			data.setContentType(SERVICE_CONTENT_TYPE);
			httpPost.setEntity(data);
			response = httpClient.execute(httpPost);
			if (response == null) {
				log.error("WebServiceHttpclient error, response is null");
				return responseStr;
			}
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != HttpStatusCodes.STATUS_CODE_OK){
				log.error("接口请求失败,请检查网络设置，响应状态码：statusCode:{}", statusCode);
				return responseStr;
			}
			HttpEntity httpEntity = response.getEntity();
			//响应正确的时候才给返回响应xml
			if (httpEntity == null) {
				log.error("WebServiceHttpclient ===>>> httpEntity is null");
				return responseStr;
			}
			responseStr = EntityUtils.toString(httpEntity, "UTF-8");
			log.info("============> resp : {}", responseStr);
			return  responseStr;
		}
		catch (Exception e) {
			log.error("============> web service invoke error: {}", e.getMessage(), e);
			return responseStr;
		}
		finally {

		}
	}

	/**
	 * @Description 包装httpclient
	 * @Author zhangzichang
	 * @Date 2021/7/15 10:53
	 * @Company CTTIC
	 * @Param []
	 * @return org.apache.http.client.HttpClient
	 **/
	private HttpClient wrapClient() {
		try{
			if(null == httpClientBuilder) {//单例模式
				synchronized (WebServiceHttpclient.class) {
					if(null == httpClientBuilder) {
						RequestConfig requestConfig = RequestConfig.custom()
							.setConnectionRequestTimeout(requestTimeout)
							.setConnectTimeout(connectTimeout)
							.setSocketTimeout(socketTimeout)
							.build();

						//采用绕过验证的方式处理https请求
						SSLContext sslcontext = createIgnoreVerifySSL();

						SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslcontext, NoopHostnameVerifier.INSTANCE);

						//设置协议http和https对应的处理socket链接工厂的对象
						Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
								.register("http", PlainConnectionSocketFactory.INSTANCE)
								.register("https", sslSocketFactory)
								.build();
						PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

						httpClientBuilder = HttpClientBuilder.create().setMaxConnTotal(50) // 连接池中最大连接数
							.setDefaultRequestConfig(requestConfig)
							.setConnectionManagerShared(true)
							.setKeepAliveStrategy(new WebServiceConnectionKeepAliveStrategy())
							.setConnectionManager(connManager);
					}
				}
			}
		}catch (Exception e){
			log.error("初始httpClientBuilder失败, msg : {}", e.getMessage(), e);
		}
		return httpClientBuilder.build();
	}

	/**
	 * 绕过验证
	 *
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
	public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
		SSLContext sc = SSLContext.getInstance("TLS");
		// 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
		X509TrustManager trustManager = new X509TrustManager() {
			@Override
			public void checkClientTrusted(
					java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
					String paramString) throws CertificateException {
			}
			@Override
			public void checkServerTrusted(
					java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
					String paramString) throws CertificateException {
			}

			@Override
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return null;
			}
		};
		sc.init(null, new TrustManager[] { trustManager }, null);
		return sc;
	}

}
